﻿// CtoLua.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>

#include "CLuaWrapper.h"

// 原函数
static int add(int x, int y)
{
	return x + y;
}

// 给Lua调用的函数
static int add_tolua(lua_State* L)
{
	printf("此处被Lua调用了!\n");
	// 此处获取Lua传过来的参数
	/* 得到参数个数 */
	int n = lua_gettop(L);
	printf("add函数(Lua脚本内)传参个数:%d个\n", n);
	//int x = 0, y = 0;
	//if (lua_isnumber(L, -1)) {
	//	y = lua_tonumber(L, -1);
	//}
	//if (lua_isnumber(L, -2)) {
	//	x =  lua_tonumber(L, -2);
	//}

	int y = luaL_checkint(L, -1);
	int x = luaL_checkint(L, -2);
	// end

	// 调用C函数
	int sum = add(x, y);
	// 结果压入Lua栈
	lua_pushnumber(L, sum);
	// end

	// 返回1个值
	return 1;
}

int test()
{
	//1.初始化Lua  
	CLuaWrapper::init();

	// 注册函数
	CLuaWrapper::reg_fun2Lua("add", add_tolua);

	// 执行Lua脚本
	CLuaWrapper::exe_lua_file("test.lua");

	// 退出关闭Lua
	CLuaWrapper::exit();

	return 0;
}


#include <vector>
using namespace std;
#include "CLua.h"
char gpCommandBuffer[254];
const char *GetCommand(void)
{
	printf("=====> ");
	return fgets(gpCommandBuffer, 254, stdin);
	puts("\n");
}

void AddFunction(CLua * pLua);
void DisFunction(CLua * pLua);

int main(void)
{
	test();

	// 打印说明
	puts("=========================");
	puts("||Lua控制台接收输入命令||");
	puts("||请输入Lua脚本,[Q]退出||");
	puts("=========================");

	CLua *pLua = new CLua;

	AddFunction(pLua);

	//initialize script
	pLua->RunScript("init.lua");

	// 处理命令
	const char *pCommand = GetCommand();
	while (_stricmp(pCommand, "Q\n") != 0)
	{
		// 将字符串传递给CLua
		int dot = 0;
		for (dot = 0; pCommand[dot]; dot++) {
			if (pCommand[dot] == '.') {
				break;
			}
		}
		if (_stricmp(pCommand + dot, ".lua") == 0) {
			if (!pLua->RunScript(pCommand)) {
				printf("错误:%s\n", pLua->GetErrorString());
			}
		}
		else {
			if (!pLua->RunString(pCommand))
			{
				printf("错误:%s\n", pLua->GetErrorString());
			}
		}

		// 获取下一个命令
		pCommand = GetCommand();
	}

	delete pLua;

	printf("按任意键退出");
	_getch();
	
	return 0;
}
/////////////////Lua Functions//////////////////////////
class luaObject {
public:
	int uid;
	int v[10];
};

vector<luaObject*> objs;
void DisFunction(CLua * pLua) {
	for (int i = 0; i < objs.size(); i++) {
		if (objs.at(i)) {
			delete objs.at(i);
		}
	}
	objs.clear();
}
////////////////////////////////////////////////
static void luaL_findfunction(lua_State *L, int idx, const char *fname) {
	const char *e;
	lua_pushvalue(L, idx);
	do {
		e = strchr(fname, '.');
		if (e == NULL) e = fname + strlen(fname);
		lua_pushlstring(L, fname, e - fname);
		lua_rawget(L, -2);
		if (lua_isnil(L, -1)) {  /* no such field? */
			lua_remove(L, -2);  /* remove previous table */
			return;
		}
		else if (lua_istable(L, -1)) {  /* field has a non-table value? */
			lua_remove(L, -2);  /* remove previous table */
			fname = e + 1;
		}
		else if (lua_isfunction(L, -1)) {
			lua_remove(L, -2);  /* remove previous table */
			//lua_pop(L, 2);  /* remove table and value */        
			return;  /* return problematic part of the name */
		}
	} while (*e == '.');
	return;
}
static int report(lua_State *L, int status, const char* function) {
	if (status && !lua_isnil(L, -1)) {
		const char *msg = lua_tostring(L, -1);
		if (msg == NULL) msg = "(error object is not a string)";
		printf("lua error:%s in %s\n", msg, function);
		lua_pop(L, 1);
	}
	return status;
}

void luaObjectCaller(lua_State *L, luaObject * obj, const char * function, const char * format, ...) {
	try {
		luaL_findfunction(L, LUA_GLOBALSINDEX, function);
		if (lua_isfunction(L, -1)) {
			//bin lua object
			luaObject ** wrapper = (luaObject**)lua_newuserdata(L, sizeof(obj));
			*wrapper = obj;
			luaL_getmetatable(L, "ObjectMetatable");
			lua_setmetatable(L, -2);
			//push parameters
			int params = 1;
			va_list ap;
			va_start(ap, format);
			const char * fm = format;
			while (*fm != 0) {
				if (*fm == '%') {
					const char * ft = fm + 1;
					while (*ft != 0) {
						if (*ft == '%') {
							fm = ft;
							break;
						}
						if (*ft == 's') {
							char * param = va_arg(ap, char*);
							lua_pushstring(L, param); 
							params++;
							fm = ft;
							break;
						}
						else if (*ft == 'd') {
							int param = va_arg(ap, int);
							lua_pushinteger(L, param);
							params++;
							fm = ft;
							break;
						}
						ft++;
					}
				}
				fm++;
			}
			va_end(ap);
			//call function
			report(L, lua_pcall(L, params, 0, 0), function);
		}
		else {
			lua_pop(L, 1);
		}
	}
	catch (...) {
		printf("------------Exception error-------------: %s\n", function);
	}
}
void luaObjectCaller(lua_State *L, luaObject * obj, const char * function, const char * format, const char * param) {
	try {
		luaL_findfunction(L, LUA_GLOBALSINDEX, function);
		if (lua_isfunction(L, -1)) {
			//bin lua object
			luaObject ** wrapper = (luaObject**)lua_newuserdata(L, sizeof(obj));
			*wrapper = obj;
			luaL_getmetatable(L, "ObjectMetatable");
			lua_setmetatable(L, -2);
			//push parameters
			int params = 1;
			va_list ap;
			const char * fm = format;
			const char * pm = param;
			char temp[512];
			while (*fm != 0 && *pm != NULL) {
				if (*fm == '%') {
					const char * ft = fm + 1;
					while (*ft != 0) {
						if (*ft == '%') {
							fm = ft;
							break;
						}
						if (*ft == 's') {
							//char * param = va_arg(ap, char*);
							int i = 0;
							for (i = 0; *pm != NULL; pm++) {
								if (*pm == ' ') {
									pm++;
									break;
								}
								temp[i++] = *pm;
							}
							temp[i] = 0;
							lua_pushstring(L, temp);
							params++;
							fm = ft;
							break;
						}
						else if (*ft == 'd') {
							//int param = va_arg(ap, int);
							int i = 0;
							for (i = 0; *pm != NULL; pm++) {
								if (*pm == ' ') {
									pm++;
									break;
								}
								temp[i++] = *pm;
							}
							temp[i] = 0;
							int iparam = 0;
							sscanf(temp, fm, &iparam);
							lua_pushinteger(L, iparam);
							params++;
							fm = ft;
							break;
						}
						ft++;
					}
				}
				fm++;
			}
			//call function
			report(L, lua_pcall(L, params, 0, 0), function);
		}
		else {
			lua_pop(L, 1);
		}
	}
	catch (...) {
		printf("------------Exception error-------------: %s\n", function);
	}
}
////////////////////////////////////////////////
LuaGlue _NewObject(lua_State *L) {
	luaObject ** wrapper = (luaObject**)lua_newuserdata(L, sizeof(luaObject*));

	luaObject * obj = new luaObject();
	obj->uid = objs.size() + 1;
	*wrapper = obj;
	objs.push_back(obj);

	luaObjectCaller(L, obj, "onload", "%s%.2d   %03d %d", "test", 1, 2);
	luaObjectCaller(L, obj, "onload", "%s%.2d   %03d %d", "test 1 2 3");

	//metatable
	luaL_getmetatable(L, "ObjectMetatable");
	lua_setmetatable(L, -2);
	return 1;
}
LuaGlue _GetObject(lua_State *L) {
	luaObject ** wrapper = (luaObject**)lua_newuserdata(L, sizeof(luaObject*));

	int index = luaL_checkint(L, 1);
	luaObject * obj = NULL;
	for (int i = 0; i < objs.size(); i++) {
		if (objs.at(i)->uid == index) {
			obj = objs.at(i);
			break;
		}
	}
	if (obj == NULL) {
		lua_pushstring(L, "GetObject: NULL wrapper");
		lua_error(L);
		return 1;
	}
	*wrapper = obj;

	//metatable
	luaL_getmetatable(L, "ObjectMetatable");
	lua_setmetatable(L, -2);
	return 1;
}
LuaGlue _GetConstants(lua_State *L) {
	lua_pushstring(L, "Constants");
	return 1;
}
////////////////////////////////////////////////
LuaGlue __GetValue(lua_State *L);
LuaGlue __SetValue(lua_State *L);
LuaGlue __GetUID(lua_State *L);
static const luaL_reg lib_funcs[] = {
	{"getUID",		__GetUID},
	{"getValue",	__GetValue},
	{"setValue",	__SetValue},
	{0, 0}
};
LuaGlue __GetUID(lua_State *L) {
	luaObject * wrapper = *(luaObject**)luaL_checkudata(L, 1, "ObjectMetatable");
	//assert(wrapper->data != NULL);
	if (wrapper == NULL) {
		lua_pushstring(L, "GetUID: NULL wrapper");
		lua_error(L);
		return 1;
	}
	lua_pushnumber(L, wrapper->uid);
	return 1;
}
LuaGlue __GetValue(lua_State *L) {
	//使用luaL_checkstring(L, -1);得到调用函数的名称
	if (lua_isstring(L, -1)) {
		const char * s = lua_tostring(L, -1);
		for (int i = sizeof(lib_funcs) / sizeof(luaL_reg) -2; i >= 0 ; i--) {
			if (_stricmp(lib_funcs[i].name, s) == 0) {
				//printf("__index:%s\n", s);
				lua_pushcfunction(L, lib_funcs[i].func);
				return 1;
			}
		}
	}
	//两种方式取得self元表
	luaObject * wrapper = NULL;
	if (false) {
		luaL_checktype(L, -1, LUA_TNUMBER);
		luaL_checktype(L, -2, LUA_TUSERDATA);
		wrapper = *(luaObject**)lua_touserdata(L, -2);
	}
	else {
		wrapper = *(luaObject**)luaL_checkudata(L, 1, "ObjectMetatable");
	}
	//assert(wrapper->data != NULL);
	if (wrapper == NULL) {
		lua_pushstring(L, "GetValue: NULL wrapper");
		lua_error(L);
		return 1;
	}
	int index = (int)(float)lua_tonumber(L, -1);
	int value = wrapper->v[index];
	lua_pushnumber(L, value);
	return 1;
}
LuaGlue __SetValue(lua_State *L) {
	//使用luaL_checkstring(L, -1);得到调用函数的名称
	if (lua_isstring(L, -1)) {
		const char * s = lua_tostring(L, -1);
		for (int i = sizeof(lib_funcs) / sizeof(luaL_reg) - 2; i >= 0; i--) {
			if (_stricmp(lib_funcs[i].name, s) == 0) {
				//printf("__newindex:%s\n", s);
			}
		}
	}
	//两种方式取得self元表
	luaObject * wrapper = NULL;
	if (false) {
		luaL_checktype(L, -1, LUA_TNUMBER);
		luaL_checktype(L, -2, LUA_TUSERDATA);
		wrapper = *(luaObject**)lua_touserdata(L, -2);
	}
	else {
		wrapper = *(luaObject**)luaL_checkudata(L, 1, "ObjectMetatable");
	}
	//assert(wrapper->data != NULL);
	if (wrapper == NULL) {
		lua_pushstring(L, "SetValue: NULL wrapper");
		lua_error(L);
		return 1;
	}
	int value = (int)(float)lua_tonumber(L, -1);
	int index = (int)(float)lua_tonumber(L, -2);
	wrapper->v[index] = value;
	return 0;
}
////////////////////////////////////////////////
void luaObjectBind(CLua *pLua) {
	//pLua->AddFunction("newObject", _NewObject);
	//pLua->AddFunction("getObject", _GetObject);

	lua_State *L = pLua->GetScriptContext();
	static const luaL_reg server_funcs[] = {
		{"newObject",	_NewObject},
		{"getObject",	_GetObject},
		{0, 0}
	};
	luaL_register(L, "server", server_funcs);
	static const luaL_reg object_funcs[] = {
		{"getConstants",	_GetConstants},
		{0, 0}
	}; 
	luaL_register(L, "object", object_funcs);

	//metatable
	//创建元表
	//调用对象函数时，通过验证其metatable名称确定参数是否合法
	luaL_newmetatable(L, "ObjectMetatable");

	//下面采用操作方式2，方式1因为不能取下标而不采用
	if (false) {
		//操作方式1
		//创建元表之后，为了实现对象的调用方式，将元表的__index字段指向自身。此时对象取下标操作将会无效
		lua_pushvalue(L, -1);
		//将在栈顶的metatable弹出
		lua_setfield(L, -2, "__index");
		//同时将函数注册到元表中，基于这些注册函数调用就可以了，
		//当使用时，使用":"调用，将会多传递一个self参数，也就是metatable，相当于面向对象调用中的this
		luaL_register(L, 0, lib_funcs);
		lua_pop(L, 1);
	}
	else {
		//操作方式2
		//如果不将元表的__index字段指向自身，则__index/__newindex表示为对象取下标操作，或者对象的直接操作
		//这种情况下，在__GetValue/__SetValue中，如果使用luaL_checkstring(L, -1);将会得到调用函数的名称
		lua_pushstring(L, "__index");
		lua_pushcfunction(L, __GetValue);
		lua_rawset(L, -3);//ObjectMetatable.__index = __GetValue
		lua_pushstring(L, "__newindex");
		lua_pushcfunction(L, __SetValue);
		lua_rawset(L, -3);//ObjectMetatable.__index = __SetValue
	}
}
////////////////////////////////////////////////

LuaGlue _Version(lua_State *L)
{
	puts("Lua控制台测试版本1.0");
	return 0;
}

LuaGlue _Add(lua_State *L) {
	int x = luaL_checkint(L, 1);
	int y = luaL_checkint(L, 2);
	printf("%d + %d=", x, y, x + y);
	lua_pushinteger(L, x + y);
	return 1;
}
int g_f = 0;
LuaGlue _Callback(lua_State* L) {
	int p1 = luaL_checkint(L, 1);
	int p2 = luaL_checkint(L, 2);

	// 存放函数到注册表中并返回引用，并且会pop栈顶的对象
	g_f = luaL_ref(L, LUA_REGISTRYINDEX);

	return 0;
}
LuaGlue _Invoke(lua_State* L) {
	if (g_f == 0) {
		return 0;
	}
	// 从注册表中读取该函数并调用
	lua_rawgeti(L, LUA_REGISTRYINDEX, g_f);
	lua_pcall(L, 0, 1, 0);
	int p1 = luaL_checkint(L, 1);
	printf("This is callback return: %d\n", p1);

	return 0;
}
//lua function registration
void AddFunction(CLua * pLua) {
	pLua->AddFunction("version", _Version);
	pLua->AddFunction("add", _Add);
	pLua->AddFunction("callback", _Callback);
	pLua->AddFunction("invoke", _Invoke);

	luaObjectBind(pLua);
}
////////////////////////////////////////////////////


// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门提示: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
